package centerberg.engine;

import java.awt.Canvas;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.util.Iterator;

/**
 * Simple renderer that draws on a {@link Canvas}.
 * Add the Canvas to some window using {@link getCanvas()}
 * to see anything. The render will draw frames as fast as
 * it can but if {@link Scene}s are sent faster than they
 * are rendered some frames will get skipped.
 * 
 * @author Andreas Svanberg
 * @deprecated Use {@link FrameRender} instead
 */
class CanvasRender extends Render
{
    private Canvas     canvas = new Canvas();
    
    private Scene      nextScene;
    private Thread     renderThread;
    
    private CanvasRender()
    {
    }
    
    /**
     * Creates a new {@code Scene} prepared for this
     * {@code Render}.
     * 
     * @return a new scene
     */
    public Scene createScene()
    {
        return new Scene();
    }
    
    /**
     * Returns the canvas which is being drawn upon.
     * 
     * @return the canvas which is being drawn upon
     */
    public Canvas getCanvas()
    {
        return canvas;
    }
    
    /**
     * Returns an instance of this render.
     * 
     * @return an instance of this render
     */
    public static CanvasRender getInstance()
    {
        return CanvasRenderHolder.INSTANCE;
    }
    
    /**
     * Returns the {@link java.awt.Canvas} which is being rendered upon.
     * 
     * @return the {@code Canvas}
     */
    Component getRenderingComponent()
    {
        return canvas;
    }
    
    /**
     * Returns the next scene to be rendered.
     * 
     * @return the next scene to be rendered
     */
    private synchronized Scene getNextScene()
    {
        Scene scene = nextScene;
        nextScene = null;
        return scene;
    }
    
    /**
     * Initializes the render. The canvas must have been
     * added to a visible window before it can be initialized.
     * 
     * @return {@code true} if the render initializes correctly
     * @see #getCanvas()
     */
    public boolean init()
    {
        try
        {
            canvas.createBufferStrategy(2);
            canvas.setIgnoreRepaint(true);
        }
        catch (Exception e)
        {
            return false;
        }
        return true;
    }
    
    /**
     * Renders the scene. If there is a scene waiting to be
     * rendered it is skipped. Starts the rendering thread
     * if it was done rendering the previous scene before
     * this one.
     * 
     * @param scene the scene to be rendered
     */
    public synchronized void render(Scene scene)
    {
        nextScene = scene;
        if (renderThread == null || !renderThread.isAlive())
        {
            renderThread = new RenderThread();
            renderThread.start();
        }
    }
    
    /**
     * The thread that carries out the rendering. It will
     * keep rendering as long as there are scenes waiting.
     */
    private class RenderThread extends Thread
    {
        public void run()
        {
            Scene scene;
            
            while ((scene = getNextScene()) != null)
            {
                Graphics2D drawGraphics = (Graphics2D)canvas.getBufferStrategy().getDrawGraphics();
                drawGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                
                for (Iterator<Drawable> it = scene.drawObjects(); it.hasNext();)
                {
                    Drawable drawObject = it.next();
                    
                    if (activeCamera != null)
                    {
                        Rectangle viewport = activeCamera.getViewport();
                        drawGraphics.setClip(viewport.x, viewport.y, viewport.width, viewport.height);
                    }
                    
                    drawGraphics.drawImage(drawObject.getTexture().getImage(),
                        drawObject.getTexture().getTransform(),
                        null
                    );
                }
                
                if (!canvas.getBufferStrategy().contentsLost())
                    canvas.getBufferStrategy().show();
                drawGraphics.dispose();
            }
        }
    }
    
    // Singleton pattern that prevents the class from
    // being initialized before it is actually needed
    private static final class CanvasRenderHolder {
        public static final CanvasRender INSTANCE = new CanvasRender();
    }
}